﻿using System;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace InstallPartner
{
    /// <summary>
    /// This class provides the business logic for configuring and installing applications.
    /// </summary>
    public class InstallHelper
    {
        #region Constants

        public const string BaseProfileName = "Base Profile";
        public const string DataSetName = "InstallPartner";
        public const string ProfileTable = "Profile";
        public const string ApplicationTable = "Application";
        public const string ProfileApplicationTable = "ProfileApplication";
        public const string IdColumn = "ID";
        public const string NameColumn = "Name";
        public const string EnumOrderColumn = "EnumOrder";
        public const string GroupColumn = "Group";
        public const string CommandColumn = "Command";
        public const string ArgumentsColumn = "Arguments";
        public const string ProfileIdColumn = "ProfileId";
        public const string ApplicationNameColumn = "ApplicationName";

        #endregion

        #region Private Attributes

        private int _processId;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="InstallHelper"/> class.
        /// </summary>
        public InstallHelper()
            : this(string.Empty)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="InstallHelper"/> class.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        public InstallHelper(string fileName)
        {
            Load(fileName);
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the default configuration file name.
        /// </summary>
        /// <value>The default configuration file name.</value>
        public static string DefaultConfigurationFileName
        {
            get
            {
                return Path.Combine(Application.StartupPath, "InstallPartner.xml");
            }
        }

        /// <summary>
        /// Gets the default lock file name.
        /// </summary>
        /// <value>The default lock file name.</value>
        public static string DefaultLockFileName
        {
            get
            {
                return Path.Combine(Application.StartupPath, "InstallPartner.lock");
            }
        }

        /// <summary>
        /// Gets a value indicating whether the application is currently in read-only mode.
        /// </summary>
        /// <value><c>true</c> if the application is currently in read-only mode; otherwise, <c>false</c>.</value>
        public static bool IsReadOnly
        {
            get
            {
                return !string.IsNullOrEmpty(LockOwner) && !Environment.UserName.Equals(LockOwner);
            }
        }

        /// <summary>
        /// Gets the current lock owner's user name.
        /// </summary>
        /// <value>The current lock owner's user name.</value>
        public static string LockOwner { get; private set; }

        /// <summary>
        /// Gets or sets the name of the file.
        /// </summary>
        /// <value>The name of the file.</value>
        public string FileName { get; set; }

        /// <summary>
        /// Gets a value indicating whether there are any unsaved changes.
        /// </summary>
        /// <value><c>true</c> if there are any unsaved changes; otherwise, <c>false</c>.</value>
        public bool IsDirty
        {
            get
            {
                return Applications.HasChanges();
            }
        }

        /// <summary>
        /// Gets the application data.
        /// </summary>
        /// <value>The application data.</value>
        public DataSet Applications { get; private set; }

        /// <summary>
        /// Gets or sets the name of the currently selected application.
        /// </summary>
        /// <value>The name of the currently selected application.</value>
        public string SelectedApplication { get; set; }

        /// <summary>
        /// Gets a <see cref="DataView"/> representation of the selected items.
        /// </summary>
        /// <value>A <see cref="DataView"/> representation of the selected items.</value>
        public DataView SelectedItems { get; private set; }

        /// <summary>
        /// Gets a <see cref="DataView"/> representation of the available profiles, sorted by name.
        /// </summary>
        /// <value>A <see cref="DataView"/> representation of the available profiles, sorted by name.</value>
        public DataView Profiles { get; private set; }

        /// <summary>
        /// Gets a value indicating whether any applications have been selected for the current profile.
        /// </summary>
        /// <value><c>true</c> if any applications have been selected for the current profile; otherwise, 
        /// <c>false</c>.</value>
        public bool HasSelectedItems
        {
            get
            {
                return SelectedItems.Count > 0;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Creates a file lock if one does not already exist.
        /// </summary>
        /// <param name="fileName">The name of the configuration file being locked.</param>
        /// <returns>The current lock owner's user name.</returns>
        public static string CreateFileLock(string fileName)
        {
            LockOwner = null;

            // Do not attempt to lock an empty filename
            if (string.IsNullOrEmpty(fileName))
            {
                return null;
            }

            if (File.Exists(DefaultLockFileName))
            {
                // The lock file exists
                var fileText = File.ReadAllText(DefaultLockFileName);

                // Do a quick check to see if the file being locked exists in the file
                if (fileText.Contains(fileName))
                {
                    // The file being locked exists so let's see who has the lock
                    var keyValuePairs = fileText.Split('~');

                    foreach (var keyValuePair in keyValuePairs)
                    {
                        var pair = keyValuePair.Split('|');

                        if (pair[0].Equals(fileName))
                        {
                            LockOwner = pair[1];
                            break;
                        }
                    }
                }

                // We have not found the configuration file in the lock file so let's add it
                if (LockOwner == null)
                {
                    LockOwner = Environment.UserName;
                    File.WriteAllText(DefaultLockFileName, string.Format("{0}~{1}|{2}", fileText, fileName, LockOwner));
                }
            }
            else
            {
                // The lock file does not exist so let's create it and add the current file being locked
                LockOwner = Environment.UserName;
                File.WriteAllText(DefaultLockFileName, string.Format("{0}|{1}", fileName, LockOwner));
            }

            // Return null if the current user owns the lock; otherwise, return the lock owner's name
            return LockOwner;
        }

        /// <summary>
        /// Deletes the file lock if it exists and belongs to the current user.
        /// </summary>
        /// <param name="fileName">The name of the configuration file being unlocked.</param>
        public static void RemoveFileLock(string fileName)
        {
            var lockOwner = Environment.UserName;
            var ownerList = new StringBuilder();

            // First, check to see if the lock file exists
            if (File.Exists(DefaultLockFileName))
            {
                // The lock file exists
                var fileText = File.ReadAllText(DefaultLockFileName);
                var keyValuePairs = fileText.Split('~');

                // Rebuild the owner list
                foreach (var keyValuePair in keyValuePairs)
                {
                    var pair = keyValuePair.Split('|');

                    // If the current key/value pair does not match the configuration file
                    // being unlocked or the owner name does not match the current owner,
                    // then keep this key/value pair
                    if (!pair[0].Equals(fileName) || !pair[1].Equals(lockOwner))
                    {
                        ownerList.AppendFormat("{0}~", keyValuePair);
                    }
                }

                if (ownerList.Length == 0)
                {
                    // There are no more owners so let's delete the file
                    File.Delete(DefaultLockFileName);
                }
                else
                {
                    // There are some owners remaining so let's rewrite the file
                    File.WriteAllText(DefaultLockFileName, ownerList.ToString().TrimEnd('~'));
                }
            }
        }

        /// <summary>
        /// Saves the installation log to a file.
        /// </summary>
        /// <param name="text">The text to be saved.</param>
        /// <remarks>The log file will be saved in the root drive - e.g. C:\InstallPartner.log</remarks>
        public static void SaveLog(string text)
        {
            File.WriteAllText(@"C:\InstallPartner.log", text);
        }

        /// <summary>
        /// Loads an application list/profile.
        /// </summary>
        /// <param name="fileName">The name of the file to load.</param>
        public void Load(string fileName)
        {
            // Unlock the current file, if there is one
            if (!string.IsNullOrEmpty(FileName))
            {
                RemoveFileLock(FileName);
            }

            FileName = fileName;

            // Lock the current file (if possible)
            CreateFileLock(FileName);

            // Create new DataSet structure
            if (Applications == null)
            {
                Applications = new DataSet(DataSetName);
            }
            else
            {
                Applications.Clear();
            }

            if (File.Exists(fileName))
            {
                // Load the data from the specified XML file
                Applications.ReadXml(fileName);

                // Setup the initial DataView used to filter profile-based selections
                SelectedItems = new DataView(
                    Applications.Tables[ProfileApplicationTable],
                    string.Empty,
                    string.Format("{0} ASC", EnumOrderColumn),
                    DataViewRowState.CurrentRows);

                // Setup the initial DataView used to sort profiles
                Profiles = new DataView(
                    Applications.Tables[ProfileTable],
                    string.Empty,
                    string.Format("{0} ASC", NameColumn),
                    DataViewRowState.CurrentRows);

                // Start out in a "non-dirty" state
                Applications.AcceptChanges();
            }
            else
            {
                DataTable profileTable;
                DataTable applicationTable;
                DataTable profileApplicationTable;

                if (!Applications.Tables.Contains(ProfileTable))
                {
                    // Create a new, empty DataSet
                    // Define the Profile table
                    profileTable = new DataTable(ProfileTable);

                    profileTable.Columns.Add(IdColumn, typeof(int));
                    profileTable.Columns.Add(NameColumn, typeof(string));

                    Applications.Tables.Add(profileTable);
                }
                else
                {
                    profileTable = Applications.Tables[ProfileTable];
                }

                // Ensure there is always a "Base" profile
                profileTable.Rows.Add(new object[] { 1, BaseProfileName });

                if (!Applications.Tables.Contains(ApplicationTable))
                {
                    // Define the Application table
                    applicationTable = new DataTable(ApplicationTable);

                    applicationTable.Columns.Add(IdColumn, typeof(int));
                    applicationTable.Columns.Add(GroupColumn, typeof(string));
                    applicationTable.Columns.Add(NameColumn, typeof(string));
                    applicationTable.Columns.Add(CommandColumn, typeof(string));
                    applicationTable.Columns.Add(ArgumentsColumn, typeof(string));

                    Applications.Tables.Add(applicationTable);
                }

                if (!Applications.Tables.Contains(ProfileApplicationTable))
                {
                    // Define the ProfileApplication table
                    profileApplicationTable = new DataTable(ProfileApplicationTable);

                    profileApplicationTable.Columns.Add(ProfileIdColumn, typeof(int));
                    profileApplicationTable.Columns.Add(ApplicationNameColumn, typeof(string));
                    profileApplicationTable.Columns.Add(EnumOrderColumn, typeof(int));

                    Applications.Tables.Add(profileApplicationTable);
                }

                // Setup the initial DataView used to filter profile-based selections
                SelectedItems = new DataView(
                    Applications.Tables[ProfileApplicationTable],
                    string.Empty,
                    string.Format("{0} ASC", EnumOrderColumn),
                    DataViewRowState.CurrentRows);

                // Setup the initial DataView used to sort profiles
                Profiles = new DataView(
                    Applications.Tables[ProfileTable],
                    string.Empty,
                    string.Format("{0} ASC", NameColumn),
                    DataViewRowState.CurrentRows);

                // Start out in a "non-dirty" state
                Applications.AcceptChanges();
            }
        }

        /// <summary>
        /// Saves this instance.
        /// </summary>
        public void Save()
        {
            if (string.IsNullOrEmpty(FileName))
            {
                throw new InvalidOperationException("You must specify a valid file name before saving.");
            }

            Applications.AcceptChanges();
            Applications.WriteXml(FileName, XmlWriteMode.WriteSchema);
        }

        /// <summary>
        /// Sets the profile.
        /// </summary>
        /// <param name="profileId">The profile id.</param>
        public void SetProfile(int profileId)
        {
            // Just a safety check to ensure the DataView exists
            if (SelectedItems == null)
            {
                SelectedItems = new DataView(
                    Applications.Tables[ProfileApplicationTable],
                    string.Empty,
                    string.Format("{0} ASC", EnumOrderColumn),
                    DataViewRowState.CurrentRows);
            }

            SelectedItems.RowFilter = string.Format("{0} = {1}", ProfileIdColumn, profileId);
        }

        /// <summary>
        /// Adds a new profile.
        /// </summary>
        /// <param name="profileName">The name of the profile.</param>
        /// <returns>The ID of the newly created profile.</returns>
        public int AddProfile(string profileName)
        {
            var id = GetNextId(ProfileTable);

            Applications.Tables[ProfileTable].Rows.Add(
                new object[] { id, profileName });

            SetProfile(id);

            return id;
        }

        /// <summary>
        /// Copies an existing profile.
        /// </summary>
        /// <param name="sourceProfileName">The name of the profile being copied.</param>
        /// <param name="destinationProfileName">The name of the new profile.</param>
        /// <returns>The ID of the newly created profile.</returns>
        public int CopyProfile(string sourceProfileName, string destinationProfileName)
        {
            // First, add the new profile
            var id = AddProfile(destinationProfileName);

            // Second, add each selection in the source profile to the destination profile
            var sourceProfileId = GetProfileId(sourceProfileName);
            var rows = Applications.Tables[ProfileApplicationTable].Select(
                string.Format("{0} = {1}", ProfileIdColumn, sourceProfileId));

            if (rows != null)
            {
                // Loop through each ApplicationProfile row for the source profile
                foreach (var row in rows)
                {
                    AddSelection(id, row[ApplicationNameColumn].ToString());
                }
            }

            return id;
        }

        /// <summary>
        /// Deletes the specified profile.
        /// </summary>
        /// <param name="profileId">The profile ID to be deleted.</param>
        public void DeleteProfile(int profileId)
        {
            var rows =
                Applications.Tables[ProfileApplicationTable].Select(
                    string.Format("{0} = {1}", ProfileIdColumn, profileId));

            // Delete each of the mapped applications for the specified profile
            if (rows != null && rows.Length > 0)
            {
                foreach (var row in rows)
                {
                    row.Delete();
                }
            }

            rows =
                Applications.Tables[ProfileTable].Select(
                    string.Format("{0} = {1}", IdColumn, profileId));

            // Delete the profile
            if (rows != null && rows.Length > 0)
            {
                rows[0].Delete();
            }
        }

        /// <summary>
        /// Retrieves the ID of the specified profile.
        /// </summary>
        /// <param name="profileName">The name of the profile.</param>
        /// <returns>The ID of the specified profile.</returns>
        public int GetProfileId(string profileName)
        {
            var rows =
                Applications.Tables[ProfileTable].Select(
                    string.Format("{0} = '{1}'", NameColumn, profileName));

            // Delete each of the mapped applications for the specified profile
            if (rows != null && rows.Length > 0)
            {
                return (int)rows[0][IdColumn];
            }

            return 0;
        }

        /// <summary>
        /// Adds a new application.
        /// </summary>
        /// <param name="group">The group this application is to be associated with.</param>
        /// <param name="name">The application name.</param>
        /// <param name="command">The command line.</param>
        /// <param name="arguments">The command line arguments.</param>
        /// <returns>The ID of the new application.</returns>
        public int AddApplication(string group, string name, string command, string arguments)
        {
            var id = GetNextId(ApplicationTable);

            Applications.Tables[ApplicationTable].Rows.Add(
                new object[] { id, group, name, command, arguments });

            return id;
        }

        /// <summary>
        /// Modifies the specified application.
        /// </summary>
        /// <param name="applicationId">The ID of the application to be modified.</param>
        /// <param name="group">The group this application is to be associated with.</param>
        /// <param name="name">The application name.</param>
        /// <param name="command">The command line.</param>
        /// <param name="arguments">The command line arguments.</param>
        public void ModifyApplication(int applicationId, string group, string name, string command, string arguments)
        {
            var rows =
                Applications.Tables[ApplicationTable].Select(
                    string.Format("{0} = {1}", IdColumn, applicationId));

            if (rows != null && rows.Length > 0)
            {
                rows[0][GroupColumn] = group;
                rows[0][NameColumn] = name;
                rows[0][CommandColumn] = command;
                rows[0][ArgumentsColumn] = arguments;
            }
        }

        /// <summary>
        /// Deletes the specified application.
        /// </summary>
        /// <param name="applicationName">The name of the application to be deleted.</param>
        public void DeleteApplication(string applicationName)
        {
            var rows =
                Applications.Tables[ProfileApplicationTable].Select(
                    string.Format("{0} = '{1}'", ApplicationNameColumn, applicationName));

            if (rows != null && rows.Length > 0)
            {
                foreach (var row in rows)
                {
                    row.Delete();
                }
            }

            rows = Applications.Tables[ApplicationTable].Select(string.Format("{0} = '{1}'", NameColumn, applicationName));

            if (rows != null && rows.Length > 0)
            {
                foreach (var row in rows)
                {
                    row.Delete();
                }
            }
        }

        /// <summary>
        /// Gets a <see cref="DataRow"/> object for the specified application.
        /// </summary>
        /// <param name="applicationName">The name of the application to retrieve data for.</param>
        /// <returns>A <see cref="DataRow"/> object for the specified application.</returns>
        public DataRow GetApplicationRow(string applicationName)
        {
            var rows = Applications.Tables[ApplicationTable].Select(string.Format("{0} = '{1}'", NameColumn, applicationName));

            return rows != null && rows.Length > 0 ? rows[0] : null;
        }

        /// <summary>
        /// Adds the selection.
        /// </summary>
        /// <param name="profileId">The profile id.</param>
        /// <param name="applicationName">Name of the application.</param>
        public void AddSelection(int profileId, string applicationName)
        {
            // Ensure the mapping doesn't already exist
            var rows =
                Applications.Tables[ProfileApplicationTable].Select(
                    string.Format(
                        "{0} = {1} AND {2} = '{3}'",
                        ProfileIdColumn,
                        profileId,
                        ApplicationNameColumn,
                        applicationName));

            if (rows == null || rows.Length == 0)
            {
                Applications.Tables[ProfileApplicationTable].Rows.Add(
                        new object[] { profileId, applicationName, GetNextEnumOrder(profileId) });
            }
        }

        /// <summary>
        /// Removes the selection.
        /// </summary>
        /// <param name="profileId">The profile id.</param>
        /// <param name="applicationName">Name of the application.</param>
        public void RemoveSelection(int profileId, string applicationName)
        {
            // Ensure the mapping exists
            var rows =
                Applications.Tables[ProfileApplicationTable].Select(
                    string.Format(
                        "{0} = {1} AND {2} = '{3}'",
                        ProfileIdColumn,
                        profileId,
                        ApplicationNameColumn,
                        applicationName));

            if (rows != null && rows.Length > 0)
            {
                rows[0].Delete();
            }
        }

        /// <summary>
        /// Determines whether an application has been selected within the specified profile.
        /// </summary>
        /// <param name="profileId">The profile ID.</param>
        /// <param name="applicationName">The name of the application.</param>
        /// <returns><c>true</c> if the application has been selected within the specified profile; 
        /// otherwise, <c>false</c>.</returns>
        public bool IsApplicationSelected(int profileId, string applicationName)
        {
            return
                Applications.Tables[ProfileApplicationTable].Select(
                    string.Format(
                        "{0} = {1} AND {2} = '{3}'",
                        ProfileIdColumn,
                        profileId,
                        ApplicationNameColumn,
                        applicationName)).Length > 0;
        }

        /// <summary>
        /// Moves the selection up.
        /// </summary>
        /// <param name="profileId">The profile id.</param>
        /// <param name="applicationName">Name of the application.</param>
        public void MoveSelectionUp(int profileId, string applicationName)
        {
            // Ensure the mapping exists
            var rows =
                Applications.Tables[ProfileApplicationTable].Select(
                    string.Format(
                        "{0} = {1} AND {2} = '{3}'",
                        ProfileIdColumn,
                        profileId,
                        ApplicationNameColumn,
                        applicationName));

            if (rows != null && rows.Length > 0)
            {
                var currEnumOrder = GetEnumOrder(profileId, applicationName) - 1;

                if (currEnumOrder >= 0)
                {
                    // Locate the row that has the enum value we want to swap out with - if there
                    // are multiple rows, then we only swap the first one
                    var swapRows = Applications.Tables[ProfileApplicationTable].Select(
                        string.Format(
                            "{0} = {1} AND {2} <= {3}",
                            ProfileIdColumn,
                            profileId,
                            EnumOrderColumn,
                            currEnumOrder),
                            string.Format("{0} DESC", EnumOrderColumn));

                    // Move the affected row "down" one
                    if (swapRows != null && swapRows.Length > 0)
                    {
                        swapRows[0][EnumOrderColumn] = currEnumOrder + 1;
                    }

                    // Move the selected row "up" one
                    rows[0][EnumOrderColumn] = currEnumOrder;
                }
            }
        }

        /// <summary>
        /// Moves the selection down.
        /// </summary>
        /// <param name="profileId">The profile id.</param>
        /// <param name="applicationName">Name of the application.</param>
        public void MoveSelectionDown(int profileId, string applicationName)
        {
            // Ensure the mapping exists
            var rows =
                Applications.Tables[ProfileApplicationTable].Select(
                    string.Format(
                        "{0} = {1} AND {2} = '{3}'",
                        ProfileIdColumn,
                        profileId,
                        ApplicationNameColumn,
                        applicationName));

            if (rows != null && rows.Length > 0)
            {
                var currEnumOrder = GetEnumOrder(profileId, applicationName) + 1;

                if (currEnumOrder < Applications.Tables[ProfileApplicationTable].Rows.Count)
                {
                    // Locate the row that has the enum value we want to swap out with - if there
                    // are multiple rows, then we only swap the first one
                    var swapRows = Applications.Tables[ProfileApplicationTable].Select(
                        string.Format(
                            "{0} = {1} AND {2} >= {3}",
                            ProfileIdColumn,
                            profileId,
                            EnumOrderColumn,
                            currEnumOrder),
                            string.Format("{0} ASC", EnumOrderColumn));

                    // Move the affected row "up" one
                    if (swapRows != null && swapRows.Length > 0)
                    {
                        swapRows[0][EnumOrderColumn] = currEnumOrder - 1;
                    }

                    // Move the selected row "down" one
                    rows[0][EnumOrderColumn] = currEnumOrder;
                }
            }
        }

        /// <summary>
        /// Starts a process and waits for it to complete.  The results of the process (i.e.
        /// any information displayed on the console) will be returned.
        /// </summary>
        /// <param name="fileName">An application or document with which to start a process.</param>
        /// <param name="arguments">Command-line arguments to pass to the application when
        /// the process starts.</param>
        /// <returns>
        /// An empty string if the push was started successfully; otherwise, the command results
        /// are returned.
        /// </returns>
        public string RunProcess(string fileName, string arguments)
        {
            var results = "The process was not successfully started!";

            try
            {
                // Create the ProcessInfo object
                var psi = new ProcessStartInfo(fileName)
                              {
                                  Arguments = arguments,
                                  UseShellExecute = false,
                                  RedirectStandardOutput = true,
                                  RedirectStandardInput = true,
                                  RedirectStandardError = true
                              };

                // Start the process
                var proc = Process.Start(psi);

                if (proc != null)
                {
                    // Attach the output for reading
                    var standardOut = proc.StandardOutput;

                    // Store the process ID in case it is cancelled
                    _processId = proc.Id;

                    // Close the process
                    proc.WaitForExit(int.MaxValue - 1);
                    proc.Close();

                    // Read the sOut to a string.
                    results = standardOut.ReadToEnd().Trim();

                    // Close the io Stream
                    standardOut.Close();
                }

                return results;
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
            finally
            {
                // Reset the process ID
                _processId = 0;
            }
        }

        /// <summary>
        /// Cancels the currently running process (if there is one).
        /// </summary>
        public void CancelProcess()
        {
            if (_processId > 0)
            {
                var process = Process.GetProcessById(_processId);

                process.Kill();
            }
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Gets the next ID.
        /// </summary>
        /// <param name="tableName">The name of the table to retrieve the next ID from.</param>
        /// <returns>The next available ID for use.</returns>
        private int GetNextId(string tableName)
        {
            int id;
            var rows = Applications.Tables[tableName].Select(string.Empty, "ID DESC");

            if (rows != null && rows.Length > 0)
            {
                id = (int)rows[0][IdColumn] + 1;
            }
            else
            {
                id = 1;
            }

            return id;
        }

        /// <summary>
        /// Gets the next enum order for the specified profile and application.
        /// </summary>
        /// <param name="profileId">The profile ID.</param>
        /// <returns>The next enum order for the specified profile and application.</returns>
        private int GetNextEnumOrder(int profileId)
        {
            int enumOrder;
            var rows = Applications.Tables[ProfileApplicationTable].Select(
                string.Format("{0} = {1}", ProfileIdColumn, profileId), string.Format("{0} DESC", EnumOrderColumn));

            if (rows != null && rows.Length > 0)
            {
                enumOrder = (int)rows[0][EnumOrderColumn] + 1;
            }
            else
            {
                enumOrder = 0;
            }

            return enumOrder;
        }

        /// <summary>
        /// Gets the enum order for the specified profile and application.
        /// </summary>
        /// <param name="profileId">The profile ID.</param>
        /// <param name="applicationName">The name of the application.</param>
        /// <returns>The enum order for the specified profile and application.</returns>
        private int GetEnumOrder(int profileId, string applicationName)
        {
            int enumOrder;
            var rows = Applications.Tables[ProfileApplicationTable].Select(
                string.Format(
                    "{0} = {1} AND {2} = '{3}'",
                    ProfileIdColumn,
                    profileId,
                    ApplicationNameColumn,
                    applicationName));

            if (rows != null && rows.Length > 0)
            {
                enumOrder = (int)rows[0][EnumOrderColumn];
            }
            else
            {
                enumOrder = 0;
            }

            return enumOrder;
        }

        #endregion
    }
}
